package com.wzy.util;

import com.wzy.annotation.Extract;
import com.wzy.annotation.JsoupDocument;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.lang.reflect.Field;
import java.util.List;

/**
 * 描述  :
 * 创建人:    Wzy
 * 创建时间: 2020-06-05  15:49
 */
public class ConnectionUtil {
    /**
     * 模仿网页请求,创建请求连接
     *
     * @param url :目标网站url
     * @return
     */
    public static Connection getConnection(String url) {
        Connection connect = Jsoup.connect(url);
        connect.header("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9");
        connect.header("Accept-Encoding", "gzip, deflate, br");
        connect.header("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6");
        connect.header("Connection", "keep-alive");
        connect.header("Host", "zhipeng0908.gitee.io");
        connect.header("User-Agent", "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Mobile Safari/537.36");
        connect.ignoreHttpErrors(true);
        return connect;
    }

    /**
     * 执行爬虫程序
     *
     * @param clazz
     * @param url
     * @param obj
     * @throws Exception
     */
    public static Object execute(Class<?> clazz, String url, Object obj) throws Exception {
        JsoupDocument annotation = clazz.getAnnotation(JsoupDocument.class);
        if (annotation == null) {
            throw new RuntimeException("该实体类不是爬虫");
        }
        String domain = annotation.domain();
        String targetUrl = annotation.targetUrl();
        //如果存在url,那么爬取的url 以这个为主
        if (StringUtils.isNotBlank(targetUrl)) {
            url = targetUrl;
        }
        //判断url是否为空
        if (StringUtils.isBlank(url)) {
            throw new RuntimeException("未指定目标url");
        }
        //获取connection
        Connection connection = getConnection(url);
        //获取指定访问方式
        Connection.Method method = annotation.method();
        //获取document
        Document document = connection.method(method).execute().parse();
        //  System.out.println(document.text());
        Element bodyElement = document.body();
        //获取类上的css选择器
        String cssQuery = annotation.cssQuery();
        //获取目标列表
        Elements entityElements = bodyElement.select(cssQuery);
        //获取该类所有字段
        Field[] declaredFields = clazz.getDeclaredFields();
        if (obj instanceof List) {
            List list = (List) obj;
            for (Element entityElement : entityElements) {
                Object object = clazz.newInstance();

                setFieldValue(declaredFields, entityElement, object);
                list.add(object);
            }
            return list;
        } else {
            //是实体类
            int size = entityElements.size();
            if (size == 0) {
                return obj;
            }
            Element entityElement = entityElements.get(0);
            setFieldValue(declaredFields, entityElement, obj);
            return obj;
        }

    }

    private static void setFieldValue(Field[] declaredFields, Element entityElement, Object object) throws IllegalAccessException {
        for (Field field : declaredFields) {
            field.setAccessible(true);
            Extract extract = field.getAnnotation(Extract.class);
            if (extract != null) {
                //不为空,需要抽取数据
                String filedQuery = extract.cssQuery();
                Elements filedElement = entityElement.select(filedQuery);
                //判断该字段的值是否从属性总获取
                String attr = extract.attr();
                if (StringUtils.isNotBlank(attr)) {
                    String attrValue = filedElement.attr(attr);
                    //处理前缀
                    String prefix = extract.prefix();
                    attrValue = attrValue.replace(prefix, "");
                    field.set(object, attrValue);

                } else {
                    //attr为空,获取contentType,决定如何取值
                    int contentType = extract.contentType();
                    if (contentType == 1) {
                        //执行text
                        String text = filedElement.text();
                        //处理前缀
                        String prefix = extract.prefix();
                        text = text.replace(prefix, "");
                        //赋值给字段
                        field.set(object, text);
                    } else if (contentType == 2) {
                        //执行html
                        String html = filedElement.html();
                        //处理前缀
                        String prefix = extract.prefix();
                        html = html.replace(prefix, "");
                        field.set(object, html);
                    } else {
                        //存在了1 ,2 之外的类型则抛出异常
                        throw new RuntimeException("字段" + field.getName() + "抽取的类型只能是1 或者 2");
                    }
                }
            }
        }
    }
}
